/**
* \file DGVTableVTK.cpp
*
* \brief This file defines a class for display data in table form using Qt.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DGVTableVTK.h"

#include <QModelIndexList>

#include "DGVFile.h"

DGVTableVTK::DGVTableVTK(QWidget *parent) : QTableWidget(parent)
{
    complexData = false;
    linkedToImage = false;
    viewChanging = false;
    verboseMode = true;
    consoleAssigned = false;
    editable = true;

    tablePrecision = 8;

    ///Create Actions
    createActions();
    complexValuedMode(false);

    createConnections();

    setAlternatingRowColors(true);
    setSortingEnabled(false);
}

DGVTableVTK::~DGVTableVTK()
{

}

void DGVTableVTK::setName(QString filename)
{
    name = filename;
    setWindowTitle(strippedNamePrefix());

    emit nameChanged(strippedNamePrefix());
}

void DGVTableVTK::setItem(int row, int column, tableType value)
{
    QString tmp;

    tmp.setNum(value,'g',tablePrecision);
    QTableWidgetItem *item = new QTableWidgetItem(tmp);
    if(editable)
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
    else
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    QTableWidget::setItem(row,column,item);
}

void DGVTableVTK::setItem(int row, int column, QString value)
{
    QTableWidgetItem *item = new QTableWidgetItem(value);

    if(editable)
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
    else
        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    QTableWidget::setItem(row,column,item);
}

tableType DGVTableVTK::getItem(int x, int y)
{
    QString tmp;

    tmp = QTableWidget::item(x,y)->text();

    return tmp.toDouble();
}

void DGVTableVTK::zapSelection()
{
    QList<QTableWidgetItem*> selection;

    selection = QTableWidget::selectedItems();
    for(int j = 0; j < selection.size(); j ++)
        selection[j]->setText("0");
}

void DGVTableVTK::delSelection()
{
    QList<QTableWidgetSelectionRange> selectedRange;
    int size = 0, start = 0;

    selectedRange = QTableWidget::selectedRanges();

    for(int j = 0; j < selectedRange.size(); j ++)
    {
        if(selectedRange[j].columnCount() < selectedRange[j].rowCount())
        {
            ///Columns selected
            size = selectedRange[j].columnCount();
            start = selectedRange[j].leftColumn();
            /*QMessageBox::warning(this, "DGV",
                    "Do you wish to delete columns and all data within it?\n"
                    "Check the data Delimiters and Try opening the file again.");*/
            for(int j = start; j < size+start; j ++)
                QTableWidget::removeColumn(j);
        }
        else
        {
            size = selectedRange[j].rowCount();
            start = selectedRange[j].topRow();

            for(int j = start; j < size+start; j ++)
                QTableWidget::removeRow(j);
        }
    }
}

void DGVTableVTK::plotSelection()
{
    Array<Array<tableType,1>,1> selectedData; //!< Array of Array is done to have variable-sized rows
    Array<Array<tableType,1>,1> selectedDataX;
    DGVPlot *tmpPlot = NULL;

    selectionToArray(selectedDataX,selectedData);

    tmpPlot = new DGVPlot(parentWidget());
    tmpPlot->setName("Selection: " + strippedName());
    //tmpPlot->createCurvePlots(selectedDataX,selectedData,"Selection Plot");
    tmpPlot->setData(selectedDataX,selectedData);
    tmpPlot->generatePlot();
    tmpPlot->show(); //!< \todo If user does not set parent of table, leak here. Use QObjectCleanupHandler perhaps.

    emit plotAvailable(tmpPlot);
}

void DGVTableVTK::fftSelection()
{
    Array<Array<complex<tableType>,1>,1> selectedData; //!< Array of Array is done to have variable-sized rows
    Array<Array<tableType,1>,1> selectedDataX;

    selectionToArray(selectedDataX,selectedData);

    emit fftSelectionAvailable(selectedData);
}

void DGVTableVTK::ifftSelection()
{
    Array<Array<complex<tableType>,1>,1> selectedData; //!< Array of Array is done to have variable-sized rows
    Array<Array<tableType,1>,1> selectedDataX;

    selectionToArray(selectedDataX,selectedData);

    emit ifftSelectionAvailable(selectedData);
}

void DGVTableVTK::fftRealSelection()
{
    Array<Array<tableType,1>,1> selectedData; //!< Array of Array is done to have variable-sized rows
    Array<Array<tableType,1>,1> selectedDataX;

    selectionToArray(selectedDataX,selectedData);

    emit fftRealSelectionAvailable(selectedData);
}

void DGVTableVTK::ifftRealSelection()
{
    Array<Array<complex<tableType>,1>,1> selectedData; //!< Array of Array is done to have variable-sized rows
    Array<Array<tableType,1>,1> selectedDataX;

    selectionToArray(selectedDataX,selectedData);

    emit ifftRealSelectionAvailable(selectedData);
}

void DGVTableVTK::surfacePlotSelection()
{
    DGVSurfacePlotVTK *plot = new DGVSurfacePlotVTK(parentWidget());

    printWarning("Warning: Surface Plot will only plot entire table.");

    plot->setName(name);
    if(consoleAssigned)
        plot->setConsole(console);
    plot->setData(DGVTableVTK::data());
    plot->generatePlot();
    plot->generateAxes();
    plot->show(); //!< \todo If user does not set parent of table, leak here. Use QObjectCleanupHandler perhaps.

    emit surfacePlotAvailable(plot);
}

void DGVTableVTK::scanSelection()
{
    bool ok;

    double interval = QInputDialog::getDouble(this, tr("Scan Interval Time"), tr("Interval (ms):"), 250, 0, 1000000, 1, &ok);

    if(ok)
    {
        DGVAnimatedScan *ani = new DGVAnimatedScan(tableData,"Scan: " + strippedName(),parentWidget());
        ani->setTimerInterval(interval);
        ani->show(); //!< \todo If user does not set parent of table, leak here. Use QObjectCleanupHandler perhaps.
        emit animationAvailable(ani);
    }
}

void DGVTableVTK::saveSelection()
{
    QFileDialog *fileSaver = new QFileDialog(this);
    int size = 0, start = 0, count = 0;

    QString filename = fileSaver->getSaveFileName(this,
                           tr("Select File Name to Save"),
                           ".",
                           tr("Data Files (*.csv)"));

    if (!filename.isEmpty())
    {
        QString tmp;
        QList<QTableWidgetItem*> selection;
        QList<QTableWidgetSelectionRange> selectedRange;

        ///Get selected items, which is put into 1D form
        selection = QTableWidget::selectedItems();
        ///Get the actual ranges selected
        selectedRange = QTableWidget::selectedRanges();
        printInfo("Selected a total of " + tmp.setNum(selection.size()) + " items.");

        ///\todo Bug: Shift selection only works for one selection. Control selection works correctly
        ///\todo Column saves may work.

        printWarning("Bug Warning: If selections not appearing in plot, use Crtl key instead of Shift key.");

        DGVFile outFile;
        QString line;
        bool ok;

        QString sep = QInputDialog::getText(this, tr("Values Separator"), tr("Separator (a comma normally): "), QLineEdit::Normal, ",", &ok);
        console->printInfo("Separator: " + sep);

        outFile.setSeparator(sep);
        if(!outFile.open(filename,"w"))
            return;

        for(int j = 0; j < selectedRange.size(); j ++)
        {
            ///Determine shape of selection
            if(selectedRange[j].columnCount() < selectedRange[j].rowCount())
            {
                size = selectedRange[j].rowCount();
                start = selectedRange[j].topRow();
            }
            else
            {
                size = selectedRange[j].columnCount();
                start = selectedRange[j].leftColumn();
            }

            for(int k = count; k < count+size; k ++)
            {
                line += selection[k]->text();
                line += sep;
            }
            outFile.writeLine(line);
            line.clear();

            count += size;
        }

        outFile.close();
    }
}

void DGVTableVTK::saveAll()
{
    QFileDialog *fileSaver = new QFileDialog(this);
    QString filename = fileSaver->getSaveFileName(this,
                           tr("Select File Name to Save"),
                           ".",
                           tr("Data Files (*.csv)"));

    if (!filename.isEmpty())
    {
        DGVFile outFile;
        QString line;
        bool ok;

        QString sep = QInputDialog::getText(this, tr("Values Separator"), tr("Separator (a comma normally): "), QLineEdit::Normal, ",", &ok);
        console->printInfo("Separator: " + sep);

        if(ok)
        {
            outFile.setSeparator(sep);
            if(!outFile.open(filename,"w"))
                return;

            for(int j = 0; j < QTableWidget::rowCount(); j ++)
            {
                for(int k = 0; k < QTableWidget::columnCount(); k ++)
                {
                    line += item(j,k)->text();
                    if(k != QTableWidget::columnCount()-1)
                        line += sep;
                }
                outFile.writeLine(line);
                line.clear();
            }

            outFile.close();
            emit recentFileAvailable(filename);
        }
    }
}

bool DGVTableVTK::loadCSV(const QString filename)
{
    int cols = 0, rows = 1;
    DGVFile inFile;
    QStringList line;
    QString tmp1, tmp2;
    bool ok;

    printInfo("Detecting CSV File Structure");

    QString sep = QInputDialog::getText(this, tr("Values Separator"), tr("Separator (a comma normally): "), QLineEdit::Normal, ",", &ok);
    console->printInfo("Separator: " + sep);

    if(ok)
    {
        inFile.setSeparator(sep);
        if(!inFile.open(filename,"r"))
            return false;

        complexValuedMode(false);
        ///Detect CSV structure
        ///\todo Test Delimiter Detection for various cases
        line = inFile.readNextLine();
        line[line.size()-1].trimmed();
        if(line[line.size()-1].isEmpty() || line[line.size()-1] == " ") //!< Check for redundant comma at end
            line.removeLast();
        cols = line.size();

        while(!inFile.atEnd())
        {
            line = inFile.readNextLine();
            rows ++;
        }
        inFile.close();

        ///Read actual file
        printInfo("Loading CSV File: " + filename + ", Table Size: " + tmp1.setNum(rows) + "x" + tmp2.setNum(cols));
        if(!inFile.open(filename,"r"))
            return false;

        tableData.resize(rows,cols);
        tableData = 0;

        setColumnCount(cols);
        setRowCount(rows);
        setName(filename);

        for(int j = 0; j < rows; j ++)
        {
            line = inFile.readNextLine();
            for(int k = 0; k < cols; k ++)
                setItem(j,k,line[k]);
            qApp->processEvents();
        }

        inFile.close();
        return true;
    }
    else
        return false;
}

void DGVTableVTK::complexValuedMode(bool complexValues)
{
    complexData = complexValues;
    absoluteMagnitudeAct->setDisabled(!complexValues);
    absoluteMagnitudeAct->setChecked(true);
    imaginaryPartAct->setDisabled(!complexValues);
    realPartAct->setDisabled(!complexValues);
    phaseAct->setDisabled(!complexValues);
}

void DGVTableVTK::linkToImageData(bool link)
{
    linkedToImage = link;
}

void DGVTableVTK::reload()
{
    viewChanging = true;
    QProgressDialog progress("Creating Table...", "Abort", 0, tableData.rows()*tableData.cols(), this);
    int counter = 0;

    progress.setWindowModality(Qt::WindowModal);
    setRowCount(tableData.rows());
    setColumnCount(tableData.cols());
    scheduleDelayedItemsLayout();
    for(int j = 0; j < tableData.rows(); j ++)
    {
        for(int k = 0; k < tableData.cols(); k ++)
        {
            progress.setValue(counter);
            setItem(j,k,tableData(j,k));
            counter ++;

            if(progress.wasCanceled())
                break;
        }
        if(progress.wasCanceled())
            break;

        qApp->processEvents();
    }
    executeDelayedItemsLayout();
    progress.setValue(tableData.rows()*tableData.cols());
    editable = true; //!< Reset editable flag
    viewChanging = false;
}

void DGVTableVTK::setData(Array<tableType,2> &values)
{
    complexValuedMode(false);
    tableData.resize(values.shape());
    tableData = values;
    reload();
}

void DGVTableVTK::setData(Array<tableType,1> &values)
{
    complexValuedMode(false);
    tableData.resize(1,values.size());
    tableData(0,Range::all()) = values;
    reload();
}

void DGVTableVTK::setData(Array<Array<tableType,1>,1> &values)
{
    int maxCols = 0, maxItems = 0;

    complexValuedMode(false);
    for(int j = 0; j < values.size(); j ++)
    {
        maxItems += values(j).size();
        if(values(j).size() > maxCols)
            maxCols = values(j).size();
    }

    tableData.resize(values.size(),maxCols);
    tableData = 0;
    for(int j = 0; j < values.size(); j ++)
        for(int k = 0; k < values(j).size(); k ++)
            tableData(j,k) = values(j)(k);
    reload();
}

void DGVTableVTK::setData(Array<complex<tableType>,2> &values)
{
    complexValuedMode(true);
    tableComplexData.resize(values.shape());
    tableComplexData = values;
    viewMagnitude(); //!< Triggers reload
}

void DGVTableVTK::setData(Array<complex<tableType>,1> &values)
{
    complexValuedMode(true);
    tableComplexData.resize(1,values.size());
    tableComplexData(0,Range::all()) = values;
    viewMagnitude(); //!< Triggers reload
}

void DGVTableVTK::setData(Array< Array<complex<tableType>,1>,1 > &values)
{
    int maxCols = 0;

    for(int j = 0; j < values.size(); j ++)
        if(values(j).size() > maxCols)
            maxCols = values(j).size();
    tableComplexData.resize(values.size(),maxCols);

    complexValuedMode(true);
    for(int j = 0; j < values.size(); j ++)
        for(int k = 0; k < values(j).size(); k ++)
            tableComplexData(j,k) = values(j)(k);
    viewMagnitude(); //!< Triggers reload
}

void DGVTableVTK::setDataSize(int rows, int cols)
{
    if(complexData)
    {
        tableComplexData.resize(rows,cols);
        tableComplexData = 0;
    }
    else
    {
        tableData.resize(rows,cols);
        tableData = 0;
    }
}

Array<tableType,2>& DGVTableVTK::data()
{
    return tableData;
}

Array<complex<imageType>,2>& DGVTableVTK::dataComplex()
{
    return tableComplexData;
}

Array<imageType,2>& DGVTableVTK::dataViewing()
{
    if(!complexData)
        return data();

    tableData.resize(tableComplexData.shape());
    if(absoluteMagnitudeAct->isChecked())
    {
        tableData = abs(tableComplexData);
        return tableData;
    }
    else if(imaginaryPartAct->isChecked())
    {
        tableData = imag(tableComplexData);
        return tableData;
    }
    else if(realPartAct->isChecked())
    {
        tableData = real(tableComplexData);
        return tableData;
    }
    else if(phaseAct->isChecked())
    {
        tableData = arg(tableComplexData);
        return tableData;
    }
    else
        return tableData;
}

void DGVTableVTK::refresh()
{
    printInfo("Refreshing Table: " + strippedName());
    ///Load from inital source
    QTableWidget::clear();
    dataViewing();
    if(complexData)
    {
        if(absoluteMagnitudeAct->isChecked())
        {
            editable = false;
            setWindowTitle(strippedNamePrefix() + "[Abs]");
        }
        else if(imaginaryPartAct->isChecked())
        {
            editable = true;
            setWindowTitle(strippedNamePrefix() + "[Imag]");
        }
        else if(realPartAct->isChecked())
        {
            editable = true;
            setWindowTitle(strippedNamePrefix() + "[Real]");
        }
        else if(phaseAct->isChecked())
        {
            editable = false;
            setWindowTitle(strippedNamePrefix() + "[Phase]");
        }
    }
    reload();
}

void DGVTableVTK::renameData()
{
    bool ok;

    QString newName = QInputDialog::getText(this, tr("Rename Data"), tr("New Name: "), QLineEdit::Normal, name, &ok);

    if(ok)
        setName(newName);
}

void DGVTableVTK::updateData(int row, int col)
{
    //if(!linkedToImage && !viewChanging) //!< Only attempt if not linked to image data and not changing view
    if(!viewChanging)
    {
        if(!complexData)
            tableData(row,col) = item(row,col)->text().toDouble();
        else
        {
            tableType tmpValue;

            if(imaginaryPartAct->isChecked())
            {
                tmpValue = tableComplexData(row,col).real();
                tableComplexData(row,col) = complex<tableType>(tmpValue,item(row,col)->text().toDouble());
            }
            else if(realPartAct->isChecked())
            {
                tmpValue = tableComplexData(row,col).imag();
                tableComplexData(row,col) = complex<tableType>(item(row,col)->text().toDouble(),tmpValue);
            }
            else if(absoluteMagnitudeAct->isChecked() || phaseAct->isChecked())
                printWarning("Ignoring changes to Absolute Magnitude and Phase. Please make changes in Imaginary and Real parts.");
        }
    }
}

QString DGVTableVTK::strippedName()
{
    return QFileInfo(name).fileName();
}

QString DGVTableVTK::strippedNamePrefix()
{
    return "Table: " + QFileInfo(name).fileName();
}

void DGVTableVTK::addToContextMenu(QVector<QMenu*> &menus)
{
    menusToAdd = menus;
}

void DGVTableVTK::setConsole(DGVConsole *newConsole)
{
    if(newConsole != NULL)
    {
        console = newConsole;
        consoleAssigned = true;
    }
}

void DGVTableVTK::addContextMenuExtras()
{
    for(int j = 0; j < menusToAdd.size(); j ++)
        contextMenu->addAction(menusToAdd[j]->menuAction());
}

void DGVTableVTK::contextMenuEvent(QContextMenuEvent *event)
{
    contextMenu = new QMenu(this); //!< Only exists for the duration of the context selection
    selectGroup = new QActionGroup(this);
    globalGroup = new QActionGroup(this);

    ///Extra (preconnected) menus to add.
    addContextMenuExtras();

    ///Global Transforms
    contextMenu->addSeparator();
    surfacePlotAct = contextMenu->addAction(tr("Surface Plot"));
    surfacePlotAct->setToolTip("Surface Plot based on scalar warping");
    surfacePlotAct->setStatusTip("Surface Plot based on scalar warping");
    globalGroup->addAction(surfacePlotAct);
    surfacePlotAct->setShortcut(tr("Ctrl+p"));

    scanAct = contextMenu->addAction(tr("Scan"));
    scanAct->setToolTip("1D Plot Scan/Animation through the rows of the table");
    scanAct->setStatusTip("1D Plot Scan/Animation through the rows of the table");
    globalGroup->addAction(scanAct);
    scanAct->setShortcut(tr("Ctrl+a"));

    contextMenu->addSeparator()->setText("Selection");

    ///Selection Options
    zapAct = contextMenu->addAction(tr("Zap to Zero"));
    zapAct->setToolTip("Set the selection value to zero, i.e., zap the data");
    zapAct->setStatusTip("Set the selection value to zero, i.e., zap the data");
    selectGroup->addAction(zapAct);
    zapAct->setShortcut(tr("Ctrl+z"));

    delAct = contextMenu->addAction(tr("Delete"));
    selectGroup->addAction(delAct);
    delAct->setShortcut(tr("Ctrl+d"));

    plotAct = contextMenu->addAction(tr("1D Plot"));
    plotAct->setToolTip("1D Line Plot of the selection");
    plotAct->setStatusTip("1D Line Plot of the selection");
    selectGroup->addAction(plotAct);
    plotAct->setShortcut(tr("Ctrl+Shift+p"));

    fft_1DAct = contextMenu->addAction(tr("&1D FFT"));
    fft_1DAct->setToolTip("1D Fast Fourier Transform (FFT) on selection");
    fft_1DAct->setStatusTip("1D Fast Fourier Transform (FFT) on selection");
    selectGroup->addAction(fft_1DAct);
    fft_1DAct->setShortcut(tr("Ctrl+f"));
    fft_1DAct->setDisabled(!complexData);

    ifft_1DAct = contextMenu->addAction(tr("&1D iFFT"));
    ifft_1DAct->setToolTip("1D Fast Fourier Transform (iFFT) on selection");
    ifft_1DAct->setStatusTip("1D Fast Fourier Transform (iFFT) on selection");
    selectGroup->addAction(ifft_1DAct);
    ifft_1DAct->setShortcut(tr("Ctrl+i"));
    ifft_1DAct->setDisabled(!complexData);

    fft_Real_1DAct = contextMenu->addAction(tr("&1D Real2Complex FFT"));
    fft_Real_1DAct->setToolTip("1D Real to Complex Fast Fourier Transform (FFT) on selection");
    fft_Real_1DAct->setStatusTip("1D Real to Complex Fast Fourier Transform (FFT) on selection");
    selectGroup->addAction(fft_Real_1DAct);
    fft_Real_1DAct->setShortcut(tr("Ctrl+Shift+f"));
    fft_Real_1DAct->setDisabled(complexData);

    ifft_Real_1DAct = contextMenu->addAction(tr("&1D Complex2Real iFFT"));
    ifft_Real_1DAct->setToolTip("1D Complex to Real Fast Fourier Transform (iFFT) on selection");
    ifft_Real_1DAct->setStatusTip("1D Complex to Real Fast Fourier Transform (iFFT) on selection");
    selectGroup->addAction(ifft_Real_1DAct);
    ifft_Real_1DAct->setShortcut(tr("Ctrl+Shift+i"));
    ifft_Real_1DAct->setDisabled(!complexData);

    ///Complex View Options
    contextMenu->addSeparator()->setText(tr("Complex View"));
    ///Abs Display
    contextMenu->addAction(absoluteMagnitudeAct);
    ///Imag Display
    contextMenu->addAction(imaginaryPartAct);
    ///Real Display
    contextMenu->addAction(realPartAct);
    ///Phase Display
    contextMenu->addAction(phaseAct);
    complexGroup->setDisabled(!complexData);

    ///Table Formatting
    contextMenu->addSeparator();
    fitColsAct = contextMenu->addAction(tr("Resize Columns to Content"));
    fitRowsAct = contextMenu->addAction(tr("Resize Rows to Content"));
    contextMenu->addSeparator();

    ///Global Options
    saveAct = contextMenu->addAction(tr("Save Selection"));
    //saveAct->setShortcut(tr("Ctrl+Shift+s"));
    saveAct->setDisabled(true); //Not implemented properly yet
    saveAllAct = contextMenu->addAction(tr("Save Entire Table"));
    saveAllAct->setShortcut(tr("Ctrl+Shift+s"));
    ///Refresh
	refreshAct = contextMenu->addAction(tr("Refresh"));
	refreshAct->setShortcut(tr("F5"));
	renameAct = contextMenu->addAction(tr("Rename"));
	renameAct->setShortcut(tr("Ctrl+r"));
    closeAct = contextMenu->addAction(tr("Close"));
    closeAct->setShortcut(tr("Ctrl+w"));

    connect(zapAct, SIGNAL(triggered()), this, SLOT(zapSelection()));
    connect(delAct, SIGNAL(triggered()), this, SLOT(delSelection()));
    connect(plotAct, SIGNAL(triggered()), this, SLOT(plotSelection()));
    connect(fft_1DAct, SIGNAL(triggered()), this, SLOT(fftSelection()));
    connect(ifft_1DAct, SIGNAL(triggered()), this, SLOT(ifftSelection()));
    connect(fft_Real_1DAct, SIGNAL(triggered()), this, SLOT(fftRealSelection()));
    connect(ifft_Real_1DAct, SIGNAL(triggered()), this, SLOT(ifftRealSelection()));
    connect(surfacePlotAct, SIGNAL(triggered()), this, SLOT(surfacePlotSelection()));
    connect(scanAct, SIGNAL(triggered()), this, SLOT(scanSelection()));
    connect(fitColsAct, SIGNAL(triggered()), this, SLOT(resizeColumnsToContents()));
    connect(fitRowsAct, SIGNAL(triggered()), this, SLOT(resizeRowsToContents()));
    connect(saveAct, SIGNAL(triggered()), this, SLOT(saveSelection()));
    connect(saveAllAct, SIGNAL(triggered()), this, SLOT(saveAll()));
    connect(refreshAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(renameAct, SIGNAL(triggered()), this, SLOT(renameData()));
    connect(closeAct, SIGNAL(triggered()), this, SLOT(close()));

    contextMenu->exec(event->globalPos());
}

void DGVTableVTK::createConnections()
{
    connect(this, SIGNAL(cellChanged(int,int)), this, SLOT(updateData(int,int))); //!< \todo Causes crash.
    connect(absoluteMagnitudeAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(imaginaryPartAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(realPartAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(phaseAct, SIGNAL(triggered()), this, SLOT(refresh()));
}

void DGVTableVTK::createActions()
{
    absoluteMagnitudeAct = new QAction(this);
    absoluteMagnitudeAct->setText(QApplication::translate("Table", "Absolute &Magnitude", 0, QApplication::UnicodeUTF8));
    absoluteMagnitudeAct->setShortcut(tr("Alt+m"));
    absoluteMagnitudeAct->setCheckable(true);

    imaginaryPartAct = new QAction(this);
    imaginaryPartAct->setText(QApplication::translate("Table", "&Imaginary Part", 0, QApplication::UnicodeUTF8));
    imaginaryPartAct->setShortcut(tr("Alt+i"));
    imaginaryPartAct->setCheckable(true);

    realPartAct = new QAction(this);
    realPartAct->setText(QApplication::translate("Table", "&Real Part", 0, QApplication::UnicodeUTF8));
    realPartAct->setShortcut(tr("Alt+r"));
    realPartAct->setCheckable(true);

    phaseAct = new QAction(this);
    phaseAct->setText(QApplication::translate("Table", "&Phase/Argument", 0, QApplication::UnicodeUTF8));
    phaseAct->setShortcut(tr("Alt+p"));
    phaseAct->setCheckable(true);

    complexGroup = new QActionGroup(this);

    complexGroup->addAction(absoluteMagnitudeAct);
    complexGroup->addAction(imaginaryPartAct);
    complexGroup->addAction(realPartAct);
    complexGroup->addAction(phaseAct);
    complexGroup->setDisabled(!complexData);
}

void DGVTableVTK::selectionToArray(Array<Array<tableType,1>,1> &xData, Array<Array<tableType,1>,1> &selectedData)
{
    QList<QTableWidgetItem*> selection;
    QList<QTableWidgetSelectionRange> selectedRange;
    int size = 0, start = 0, count = 0;

    ///Get selected items, which is put into 1D form
    selection = QTableWidget::selectedItems();
    ///Get the actual ranges selected
    selectedRange = QTableWidget::selectedRanges();
    ///\todo Column plot doesn't work.

    printWarning("Bug Warning!: If selections do not appear in operations, use Crtl key instead of Shift key.");
    ///Copy the data ordered row wise
    if(selectedRange.size() == 1 && selection.size() != rowCount() && selection.size() != columnCount()) ///User has selected using shift key (continuous selection)
    {
        ///Break up row wise
        printWarning("Detected a large continuous selection.");

        bool rowsOk;

        printWarning("Applying operation row/column-wise. Enter the size of the ordering.");
        int mod = QInputDialog::getInteger(this, tr("Row/Column Length"),
                                  tr("Row/Column Length of Data:"), columnCount(), 0, columnCount()*rowCount(), 1, &rowsOk);

        if(rowsOk && mod <= selection.size())
        {
            int noOfRows = static_cast<int>(selection.size()/mod);

            ///Resize the Blitz arrays
            selectedData.resizeAndPreserve(noOfRows);
            xData.resizeAndPreserve(noOfRows);

            for(int j = 0; j < noOfRows; j ++)
            {
                selectedData(j).resizeAndPreserve(mod);
                xData(j).resizeAndPreserve(mod);
                for(int k = 0; k < mod; k ++)
                {
                    selectedData(j)(k) = selection[j*mod+k]->text().toDouble(); //! \todo Table could be truncating based on precision!
                    xData(j)(k) = k;
                }
            }
        }
    }
    else
    {
        printWarning("Detected a fragmented or full selection. Applying operation one at a time.");
        ///Resize the Blitz arrays
        selectedData.resizeAndPreserve(selectedRange.size());
        xData.resizeAndPreserve(selectedRange.size());
        for(int j = 0; j < selectedRange.size(); j ++)
        {
            ///Determine shape of selection
            if(selectedRange[j].columnCount() < selectedRange[j].rowCount())
            {
                size = selectedRange[j].rowCount();
                start = selectedRange[j].topRow();
            }
            else
            {
                size = selectedRange[j].columnCount();
                start = selectedRange[j].leftColumn();
            }

            selectedData(j).resizeAndPreserve(size);
            xData(j).resizeAndPreserve(size);
            for(int k = count; k < count+size; k ++)
            {
                selectedData(j)(k-count) = selection[k]->text().toDouble();
                xData(j)(k-count) = start+(k-count);
            }
            count += size;
        }
    }
}

void DGVTableVTK::selectionToArray(Array<Array<tableType,1>,1> &xData, Array<Array<complex<tableType>,1>,1> &selectedData)
{
    QList<QTableWidgetItem*> selection;
    QList<QTableWidgetSelectionRange> selectedRange;
    int size = 0, start = 0, count = 0;

    /*///Get selected items, which is put into 1D form
    selection = QTableWidget::selectedItems();
    ///Get the actual ranges selected
    selectedRange = QTableWidget::selectedRanges();
    ///Resize the Blitz arrays
    selectedData.resizeAndPreserve(selectedRange.size());
    xData.resizeAndPreserve(selectedRange.size());
    ///\todo Column plot doesn't work.

    printWarning("Bug Warning!: If selections do not appear in operations, use Crtl key instead of Shift key.");
    printWarning("Bug Warning!: Multiple selections involving complex table values do not work correctly.");
    ///Copy the data ordered row wise
    for(int j = 0; j < selectedRange.size(); j ++)
    {
        ///Determine shape of selection
        if(selectedRange[j].columnCount() < selectedRange[j].rowCount())
        {
            size = selectedRange[j].rowCount();
            start = selectedRange[j].topRow();
        }
        else
        {
            size = selectedRange[j].columnCount();
            start = selectedRange[j].leftColumn();
        }

        selectedData(j).resizeAndPreserve(size);
        xData(j).resizeAndPreserve(size);
        for(int k = count; k < count+size; k ++)
        {
            selectedData(j)(k-count) = tableComplexData(j,k-count); //!< \todo Not working correctly for more than one selection
            xData(j)(k-count) = start+(k-count);
        }
        count += size;
    }*/

    ///Get selected items, which is put into 1D form
    selection = QTableWidget::selectedItems();
    ///Get the actual ranges selected
    selectedRange = QTableWidget::selectedRanges();
    ///\todo Column plot doesn't work.
    ///\todo Untested since change

    printWarning("Bug Warning!: If selections do not appear in operations, use Crtl key instead of Shift key.");
    ///Copy the data ordered row wise
    if(selectedRange.size() == 1 && selection.size() != rowCount() && selection.size() != columnCount()) ///User has selected using shift key (continuous selection)
    {
        ///Break up row wise
        printWarning("Detected a large continuous selection.");

        bool rowsOk;

        printWarning("Applying operation row/column-wise. Enter the size of the ordering.");
        int mod = QInputDialog::getInteger(this, tr("Row/Column Length"),
                                  tr("Row/Column Length of Data:"), rowCount(), 0, columnCount()*rowCount(), 1, &rowsOk);

        if(rowsOk && mod <= selection.size())
        {
            int noOfRows = static_cast<int>(selection.size()/mod);

            ///Resize the Blitz arrays
            selectedData.resizeAndPreserve(noOfRows);
            xData.resizeAndPreserve(noOfRows);

            for(int j = 0; j < noOfRows; j ++)
            {
                selectedData(j).resizeAndPreserve(mod);
                xData(j).resizeAndPreserve(mod);
                for(int k = 0; k < mod; k ++)
                {
                    selectedData(j)(k) = tableComplexData(j,k); //! \todo Table could be truncating based on precision!
                    xData(j)(k) = k;
                }
            }
        }
    }
    else
    {
        printWarning("Detected a fragmented or full selection. Applying operation one at a time.");
        ///Resize the Blitz arrays
        selectedData.resizeAndPreserve(selectedRange.size());
        xData.resizeAndPreserve(selectedRange.size());
        for(int j = 0; j < selectedRange.size(); j ++)
        {
            ///Determine shape of selection
            if(selectedRange[j].columnCount() < selectedRange[j].rowCount())
            {
                size = selectedRange[j].rowCount();
                start = selectedRange[j].topRow();
            }
            else
            {
                size = selectedRange[j].columnCount();
                start = selectedRange[j].leftColumn();
            }

            selectedData(j).resizeAndPreserve(size);
            xData(j).resizeAndPreserve(size);
            for(int k = count; k < count+size; k ++)
            {
                selectedData(j)(k-count) = tableComplexData(j,k);
                xData(j)(k-count) = start+(k-count);
            }
            count += size;
        }
    }
}

void DGVTableVTK::printError(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printError(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}

void DGVTableVTK::printWarning(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printWarning(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}

void DGVTableVTK::printInfo(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printInfo(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}
